<template>
  <!-- picture-card -->
  <UploadImageSwiper v-bind="$attrs" v-if="isImageSwiper"></UploadImageSwiper>
  <span
    v-bind="$attrs"
    class="c-upload"
    @paste="pasteImg"
    @mouseenter="mouseenter"
    @mouseleave="mouseleave"
    :class="{
      small: props.small,
      middle: props.middle,
      [$attrs.class]: true
    }"
    v-else-if="props.listType == 'picture-card'"
  >
    <draggable
      v-model="fileList"
      item-key="index"
      v-if="!outputOrigin && showUploadList"
      class="c-upload-list"
      draggable=".c-upload-item"
      @end="onDragEnd"
    >
      <template #item="{ element, index }">
        <div class="c-upload-item" :class="element.status" :key="element.uid">
          <a-spin :spinning="element.loading">
            <span class="c-upload-item-image" v-if="element.ossUrl">
              <video
                v-if="props.type === 'video' || isVideo(element.ossUrl)"
                x5-playsinline=""
                playsinline=""
                webkit-playsinline=""
                :id="'video' + index"
                muted
                preload="auto"
                style="width: 100%; height: 100%"
                :poster="generateVideoPoster(element.ossUrl)"
              >
                <source :src="element.ossUrl" />
                Your browser does not support the video tag.
              </video>
              <!-- 去除是否为图片的校验是因为有的链接没有图片后缀 -->
              <div class="h-full contents" v-else>
                <img
                  v-if="islazyLoad"
                  v-lazyLoad="element?.ossUrl?.trim()"
                  data-w="170"
                  importance="high"
                  :alt="element.ossName"
                />
                <img
                  v-else
                  :src="
                    element?.ossUrl?.trim() +
                    '?x-oss-process=image/format,webp/resize,w_170'
                  "
                  data-w="170"
                  importance="high"
                  :alt="element.ossName"
                />
              </div>
              <!-- 
              <div v-else>
                <FileTextOutlined style="font-size: 28px" />
              </div> -->
            </span>
          </a-spin>

          <span class="c-upload-item-action" v-if="element.ossUrl">
            <EditOutlined
              v-if="isDrawing && !disabled"
              @click="handleDrawImg(element, index)"
            />
            <EyeOutlined @click="handlePreview(element, index)" />
            <DownloadOutlined
              class="mr10 ml10"
              v-if="showDownload"
              @click="downloadImg(element)"
            />
            <a-popconfirm
              title="确定删除图片?"
              ok-text="确定"
              cancel-text="取消"
              @confirm="onDelete(index)"
            >
              <DeleteOutlined v-if="!props.disabled && !element.disabled" />
            </a-popconfirm>
          </span>
        </div>
      </template>
    </draggable>
    <label
      role="button"
      @drop="dropEvent"
      @dragover="allowDrop"
      class="c-upload-item c-upload-select"
      v-if="
        !props.disabled &&
        state.fileList.length < props.limitNum &&
        props.showUpload
      "
    >
      <div class="tac">
        <PlusOutlined />
        <slot></slot>
      </div>

      <input
        type="file"
        class="uploadImg"
        :accept="state.accept"
        :disabled="props.disabled"
        :multiple="multiple"
        @change="handlerChange"
        style="display: none"
      />
      <div style="z-index: -1; overflow: auto" class="pa w100p h100p">
        <input ref="inputPaste" type="text" class="w100p h100p" />
      </div>
    </label>
  </span>
  <!-- 拖拽 text -->
  <div
    class="c-upload-drag"
    @paste="pasteImg"
    @mouseenter="mouseenter"
    @mouseleave="mouseleave"
    :class="$attrs.class"
    v-else
  >
    <label
      role="button"
      v-if="!props.disabled && fileList.length !== limitNum"
      class="c-upload-drag-select"
      :class="isDragger ? '' : props.listType"
      @drop="dropEvent"
      @dragover="allowDrop"
    >
      <template v-if="isDragger">
        <inbox-outlined
          style="font-size: 36px; color: #40a9ff"
        ></inbox-outlined>
        <span> 点击或将文件拖拽到这里上传</span>
      </template>
      <template v-else>
        <slot> <UploadOutlined class="mr10" /> Click to Upload</slot>
      </template>

      <input
        type="file"
        class="uploadImg"
        :accept="state.accept"
        :multiple="multiple"
        @change="handlerChange"
        style="display: none"
      />
      <div
        style="z-index: -1; overflow: auto"
        class="pa w100p h100p"
        v-if="isDragger"
      >
        <input ref="inputPaste" type="text" class="w100p h100p" />
      </div>
    </label>
    <div class="c-upload-drag-list" v-if="fileList.length">
      <div
        class="c-upload-drag-item"
        v-for="(element, index) in fileList"
        :key="element.uid"
      >
        <a-spin
          :spinning="!element.ossUrl"
          size="small"
          :tip="`${element.ossName}上传中...`"
        >
          <span class="c-upload-drag-item-con" v-if="element.ossUrl">
            <PaperClipOutlined class="mr10" style="color: #40a9ff" />
            <span class="f1 filename">
              <a-tooltip>
                <template #title>
                  <span style="word-break: break-all">{{
                    element.ossName
                  }}</span>
                </template>
                {{ element.ossName }}
              </a-tooltip>
            </span>
            <span v-if="element.operator" class="mr50">{{
              element.operator
            }}</span>
            <span v-if="element.createTime" class="mr20">{{
              element.createTime
            }}</span>
            <div class="c-upload-drag-item-action">
              <DownloadOutlined
                class="mr10 ml10"
                @click="downloadImg(element)"
              />
              <a-popconfirm
                :title="delTip"
                ok-text="确定"
                cancel-text="取消"
                @confirm="onDelete(index)"
              >
                <DeleteOutlined v-if="!disabled" />
              </a-popconfirm>
            </div>
          </span>
        </a-spin>
      </div>
    </div>
  </div>
  <!-- 视频播放 -->
  <a-modal
    v-model:visible="videoVisible.visible"
    title="查看视频"
    okText="确定"
    cancelText="取消"
    width="500px"
    destroyOnClose
  >
    <video controls autoplay loop style="width: 100%; height: 100%">
      <source :src="videoVisible.url" />
      Your browser does not support the video tag.
    </video>
  </a-modal>
  <preview
    v-if="state.isPreview && !previewMountGlobal"
    v-model:visible="state.isPreview"
    :file="state.previewImage"
    :fileList="state.fileList"
    :type="props.type"
  ></preview>
  <Sketchpad ref="sketchpadRef" />
</template>
<script setup>
import {
  PlusOutlined,
  EyeOutlined,
  DownloadOutlined,
  DeleteOutlined,
  InboxOutlined,
  UploadOutlined,
  PaperClipOutlined,
  // FileTextOutlined,
  EditOutlined
} from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import { reactive, watch, computed, ref, onBeforeUnmount, provide } from 'vue'
import { downloadImg } from 'utils/file.js'
import { OssUploadFile } from 'utils/oss.js'
import draggable from 'vuedraggable'
import preview from './preview.vue'
import Sketchpad from './business/Sketchpad'
import UploadImageSwiper from './business/OssUpload/UploadImageSwiper.vue'
import { createGlobalComponent } from './createComponent.js'
import { isVideo, generateVideoPoster } from 'utils/utils.js'
const props = defineProps({
  delTip: {
    type: String,
    default: '是否确定删除图片？'
  },
  type: {
    type: String,
    default: null
  },
  showCount: {
    type: Number,
    default: 0
  },
  showUpload: {
    type: Boolean,
    default: true
  },
  isDragger: {
    type: Boolean,
    default: true
  },
  files: {
    type: Array,
    default: () => []
  },
  showDownload: {
    type: Boolean,
    default: true
  },
  limitNum: {
    type: Number,
    default: 30
  },
  size: {
    type: Number,
    default: 50
  },
  accept: {
    type: String,
    default: ''
  },
  listType: {
    type: String,
    default: 'text'
  },
  middle: {
    type: Boolean,
    default: false
  },
  small: {
    type: Boolean,
    default: false
  },
  disabled: {
    type: Boolean,
    default: false
  },
  multiple: {
    type: Boolean,
    default: true
  },
  // 原文件输出
  outputOrigin: {
    type: Boolean,
    default: false
  },
  showErrorFile: {
    type: Boolean,
    default: false
  },
  us: {
    type: Boolean,
    default: false
  }, // 是否全局预览
  previewMountGlobal: {
    type: Boolean,
    default: false
  },
  // 是否加速，生产的加速，非生产的不加速
  isAccelerate: {
    type: Boolean,
    default: false
  },
  showUploadList: {
    type: Boolean,
    default: true
  },
  islazyLoad: {
    type: Boolean,
    default: true
  },
  isDrawing: {
    type: Boolean,
    default: false
  },
  isImageSwiper: {
    type: Boolean,
    default: false
  },
  isClient: {
    type: Boolean,
    default: false
  }
})
const emit = defineEmits(['update:files', 'change', 'changeFileList'])
const state = reactive({
  fileList: [],
  accept: '',
  isPreview: false,
  previewImage: '',
  previewDom: null,
  oss: null
})
const sketchpadRef = ref(null)
const videoVisible = reactive({
  visible: false,
  url: ''
})
// 显示指定个数
const fileList = computed(() => {
  let arr = []
  const len = state.fileList.length
  if (len && props.showCount && len > props.showCount) {
    const fileList1 = [...state.fileList]
    arr = fileList1.splice(0, props.showCount)
  } else {
    arr = [...state.fileList]
  }
  return arr
})
const getSuffix = url => {
  return url.substring(url.lastIndexOf('.') + 1)
}
const beforeUpload = file => {
  return new Promise((resolve, reject) => {
    // if (state.fileList.length + fileList.length > props.limitNum) {
    //   message.error('最多上传' + props.limitNum + '个文件')
    //   reject(false)
    //   return
    // }
    const isLmt = file.size / 1024 / 1024 < props.size
    if (!isLmt) {
      const showSize =
        +props.size < 1
          ? parseInt(props.size * 1024) + 'KB!'
          : props.size + 'MB!'
      message.error('最大限制上传资源大小为 ' + showSize)
      reject(false)
      return
    }
    resolve(true)
  })
}
const handlerChange = e => {
  customRequest(e.target.files)
  e.target.value = ''
}

const customRequest = files => {
  const fileList = Array.from(files)
  if (state.fileList.length + fileList.length > props.limitNum) {
    message.error('最多上传' + props.limitNum + '个文件')
    return
  }
  if (props.outputOrigin) {
    const originFiles = []
    fileList.forEach(async (file, index) => {
      const isValid = await beforeUpload(file, fileList)
      if (isValid) {
        originFiles.push(file)
      }
      if (index === fileList.length - 1) {
        emit('change', originFiles)
      }
    })

    return false
  }
  fileList.map(async file => {
    const isValid = await beforeUpload(file, fileList)
    if (isValid) {
      file.uid = Math.random()
      file.ossName = file.name
      file.status = 'uploading'
      file.loading = true
      handleUpload(file)
    }
  })
}
const getOssRegion = () => {
  const isProd = process.env.VUE_APP_MODE === 'production'
  if (props.us) {
    return 'oss-us-west-1'
  } else if (isProd) {
    if (props.isClient) {
      return 'oss-accelerate-photo'
    }
    return 'oss-accelerate'
  }
  return 'oss-cn-hangzhou'
}
const replaceURL = url => {
  const isProd = process.env.VUE_APP_MODE === 'production'
  if (props.us) {
    return url.replace(
      'cs-oss-production.oss-us-west-1.aliyuncs.com',
      'cdn-review.cupshe.com'
    )
  } else if (isProd) {
    return url
      .replace('cs-scm.oss-accelerate.aliyuncs.com', 'oss-scm-prod.kapeixi.cn')
      .replace(
        'cs-scm-photo.oss-accelerate.aliyuncs.com',
        'oss-photo.kapeixi.cn'
      )
  }
  return url
}
// 上传文件
const handleUpload = async file => {
  state.fileList.push(file)

  const idx = state.fileList.findIndex(item => {
    return item.uid === file.uid
  })
  const region = getOssRegion()
  if (!state.oss) {
    state.oss = OssUploadFile.getInstance(region)
  }
  const result = await state.oss.put(file).catch(() => {
    return null
  })
  state.fileList[idx] && (state.fileList[idx].loading = false)
  if (result && result.res.status === 200) {
    const url = replaceURL(result.res.requestUrls[0])
    state.fileList[idx] = {
      uid: file.uid,
      ossName: file.name,
      ossType: url && getSuffix(url),
      ossUrl: url,
      status: 'success',
      loading: false
    }
  } else {
    props.showErrorFile
      ? (state.fileList[idx].status = 'exception')
      : onDelete(idx)
    message.error('图片上传失败，请重试')
  }

  updateValue()
}
// 删除文件
const onDelete = index => {
  state.fileList.splice(index, 1)
  updateValue()
}
const onDragEnd = evt => {
  evt.preventDefault()
  const oldValue = state.fileList[evt.oldIndex]
  // const newValue = state.fileList[evt.newIndex]
  // state.fileList[evt.newIndex] = oldValue
  // state.fileList[evt.oldIndex] = newValue
  state.fileList.splice(evt.oldIndex, 1)
  state.fileList.splice(evt.newIndex, 0, oldValue)
  updateValue()
}
// 双向更新数据
const updateValue = () => {
  emit('update:files', state.fileList)
}
const dropEvent = e => {
  e.stopPropagation()
  e.preventDefault()
  customRequest(e.dataTransfer.files)
}
const allowDrop = e => {
  e.stopPropagation()
  e.preventDefault()
}
// 复制粘贴图片
const pasteImg = e => {
  if (state.fileList.length >= props.limitNum) return

  const clipboardData = e.clipboardData || window.clipboardData
  const items = clipboardData.items
  let file = null
  // 搜索剪切板items
  for (let i = 0; i < items.length; i++) {
    if (items[i].type.indexOf('image') !== -1) {
      file = items[i].getAsFile()
      // break
    } else {
      // clipboardData.clearData(items[i].type)
      const str = clipboardData.getData(items[i].type)
      console.log(str)
    }
  }
  if (!file) {
    message.error('请粘贴图片')
    return
  }
  customRequest([file])
}
const inputPaste = ref(null)
const mouseleave = () => {
  inputPaste.value?.blur()
}
const mouseenter = () => {
  inputPaste.value?.focus()
}
const handlePreview = (file, index) => {
  console.log(index)
  // if (props.type === 'video') {
  //   const myVideo = document.querySelector('#video' + index)
  //   myVideo.requestFullscreen()
  //   console.log('video')
  // } else {
  if (isVideo(file.ossUrl)) {
    videoVisible.url = file.ossUrl
    videoVisible.visible = true
    return
  }
  state.previewImage = file
  state.isPreview = true
  if (props.previewMountGlobal) {
    state.previewDom?.remove()
    state.previewDom = createGlobalComponent(preview, {
      visible: true,
      file: state.previewImage,
      fileList: state.fileList,
      className: 'previewGlobal',
      close: () => {
        state.isPreview = false
        state.previewDom.remove()
        state.previewDom = null
      }
    })
  }
  // }
}
const uploadToOss = async file => {
  const region = getOssRegion()
  if (!state.oss) {
    state.oss = OssUploadFile.getInstance(region)
  }
  const { res } = await state.oss.put(file)

  return new Promise((resolve, reject) => {
    if (res.status === 200) {
      const url = replaceURL(res.requestUrls[0])
      resolve(url)
    } else {
      reject(res)
    }
  })
}
const handleDrawImg = async (file, index) => {
  sketchpadRef.value.open(file.ossUrl, async ({ blob }) => {
    const name =
      file.ossName.indexOf('.') > -1
        ? file.ossName
        : file.ossUrl?.split('/').pop()
    const imgFile = new File([blob], name, {
      type: blob.type
    })
    const url = await uploadToOss(imgFile)
    state.fileList[index] = {
      ...state.fileList[index],
      ...{
        uid: file.uid,
        ossName: file.ossName,
        ossType: url && getSuffix(url),
        ossUrl: url
      }
    }
    message.success('保存成功')
  })
}
onBeforeUnmount(() => {
  if (props.previewMountGlobal) {
    state.previewDom?.remove()
  }
})
watch(
  () => props.listType,
  val => {
    // if (val === 'picture-card') {
    //   state.accept = '.png,.jpg,.jpeg,.gif,.jfif,.webp'
    // } else {
    //   state.accept = props.accept
    // }
    if (
      val === 'picture-card' &&
      props.type !== 'video' &&
      props.type !== 'all'
    ) {
      state.accept = 'image/*'
    } else {
      state.accept = props.accept
    }
  },
  {
    immediate: true
  }
)
watch(
  () => props.files,
  (newValue, oldValue) => {
    if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
      const fileList = []
      newValue &&
        newValue.forEach(item => {
          fileList.push({
            uid: item.uid || Math.random(),
            ossUrl: item.ossUrl || '',
            status: item.status || (item.ossUrl ? 'success' : ''),
            loading: item.loading || false,
            ossName: item.ossName || item.name || '',
            ossType: item.ossUrl && getSuffix(item.ossUrl),
            disabled: item.disabled || false,
            createTime: item.createTime,
            operator: item.operator
          })
        })
      state.fileList = fileList
    }
  },
  {
    immediate: true,
    deep: true
  }
)
watch(
  () => state.fileList,
  val => {
    const isloading = val.some(item => !item.ossUrl)
    emit('changeFileList', isloading, val)
    updateValue()
  },
  {
    deep: true
  }
)
provide('ossUpload', {
  state,
  props,
  fileList,
  pasteImg,
  mouseenter,
  mouseleave,
  handlerChange,
  handlePreview,
  onDelete,
  allowDrop,
  dropEvent
})
</script>
<style lang="scss" scoped>
.c-upload {
  // display: flex;
  // flex-wrap: wrap;

  margin: 0;
  padding: 0;
  &::after {
    content: '';
    display: block;
    clear: both;
  }
  &-item {
    display: flex;
    width: 100px;
    height: 100px;
    margin-right: 8px;
    margin-bottom: 8px;
    float: left;
    // padding: 8px;
    align-items: center;
    justify-content: center;
    background-color: #fafafa;
    border: 1px solid #d9d9d9;
    position: relative;
    border-radius: 2px;
    &:hover {
      cursor: pointer;
      .c-upload-item-image::before {
        opacity: 1;
      }
      .c-upload-item-action {
        opacity: 1;
      }
    }
    &-action {
      position: absolute;
      top: 50%;
      z-index: 10;
      white-space: nowrap;
      transform: translate(-50%, -50%);
      opacity: 0;
      left: 50%;
      transition: all 0.3s;
      :deep .anticon {
        z-index: 10;
        width: 16px;
        margin: 0 4px;
        color: rgba(255, 255, 255, 0.85);
        font-size: 16px;
        cursor: pointer;
        transition: all 0.3s;
      }
    }
    :deep .ant-spin-container,
    .ant-spin-nested-loading {
      height: 100%;
      width: 100%;
    }
    &-image {
      position: relative;
      height: 100%;
      overflow: hidden;
      display: flex;
      align-items: center;
      justify-content: center;
      &::before {
        position: absolute;
        z-index: 1;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        opacity: 0;
        transition: all 0.3s;
        content: ' ';
      }

      img {
        width: auto;
        height: auto;
        max-height: 100%;
        max-width: 100%;
      }
    }
  }
  &-select {
    float: left;
    border: 1px dashed #d9d9d9;
    &:hover {
      border-color: #1890ff;
    }
  }
  &.small {
    .c-upload-item {
      width: 60px;
      height: 60px;
      // padding: 4px;
      &-action {
        :deep .anticon {
          z-index: 10;
          width: 14px;
          font-size: 14px;

          &.anticon-delete {
            position: absolute;
            top: -25px;
            right: -15px;
            color: red;
          }
        }
      }
    }
  }
  &.middle {
    .c-upload-item {
      width: 80px;
      height: 80px;
      // padding: 6px;
    }
  }
}
.c-upload-drag {
  position: relative;
  width: 100%;
  height: 100%;
  :deep .ant-spin-container,
  .ant-spin-nested-loading {
    height: 100%;
    width: 100%;
  }
  :deep(.ant-spin-spinning) {
    display: flex;
    align-items: center;
    font-size: 12px;
  }
  &-select {
    display: flex;
    padding: 0;
    height: 60px;
    justify-content: center;
    align-items: center;
    width: 100%;
    text-align: center;
    background: #fafafa;
    border: 1px dashed #d9d9d9;
    border-radius: 2px;
    cursor: pointer;
    position: relative;
    transition: border-color 0.3s;
    &.text {
      width: unset;
      height: unset;
      background-color: #fff;
      border: 1px solid #d9d9d9;
      width: 150px;
      padding: 8px;
    }
    &:hover {
      cursor: pointer;
    }
  }
  &-list {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
  }
  &-item {
    padding: 4px 12px 4px 4px;
    position: relative;
    margin-top: 8px;
    font-size: 14px;
    transition: background-color 0.3s;
    width: 33.3%;
    &-con {
      display: flex;
      height: 100%;
      align-items: center;
    }
    &-action {
      opacity: 0;
    }
    &:hover {
      background-color: #f5f5f5;
      .c-upload-drag-item-action {
        opacity: 1;
      }
    }
    .filename {
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
    }
  }
}

.col2 {
  .c-upload-drag-item {
    width: 50%;
  }
}
.col1 {
  .c-upload-drag-item {
    width: 100%;
  }
}
.col3 {
  .c-upload-drag-item {
    width: 33.3%;
  }
}
</style>
<style>
.ant-popover button {
  animation: none;
}
</style>
